package com.gitee.wsl.android.ui.ext

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.animation.PropertyValuesHolder
import android.animation.TimeInterpolator
import android.animation.TypeEvaluator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Path
import android.graphics.PointF
import android.graphics.drawable.Drawable
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.AccelerateInterpolator
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.view.animation.AnticipateInterpolator
import android.view.animation.AnticipateOvershootInterpolator
import android.view.animation.BounceInterpolator
import android.view.animation.CycleInterpolator
import android.view.animation.DecelerateInterpolator
import android.view.animation.Interpolator
import android.view.animation.LinearInterpolator
import android.view.animation.OvershootInterpolator
import android.view.animation.PathInterpolator
import android.view.animation.Transformation
import android.view.animation.TranslateAnimation
import androidx.annotation.AnimRes
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gitee.wsl.android.ui.ext.AnimationExt.loadAnimation
import com.gitee.wsl.android.ui.ext.type.LineExt.calculateBezierPointForCubic
import com.gitee.wsl.android.ui.ext.type.LineExt.calculateBezierPointForQuadratic

import com.gitee.wsl.ext.base.isTrue

object AnimationExt {
    private const val ANIM_DURATION = 200
    private const val SCALE = 1.2f
    private const val SCALE_X = "scaleX"
    private const val SCALE_Y = "scaleY"
    private const val TRANSLATION_X = "translationX"
    private const val TRANSLATION_Y = "translationY"
    private const val TRANSLATION_Z = "translationZ"
    private const val ALPHA = "alpha"

    private val animationArray by lazy { SparseArray<Animation>() }

    private val animationArrayKey by lazy {
        animationArray.hashCode()
    }

    private val lifecycleObserver by lazy {
        { _: LifecycleOwner, event: Lifecycle.Event ->
            if (event == Lifecycle.Event.ON_DESTROY) {
                animationArray.clear()
            }
            true
        }
    }

    fun loadAnimation(context: Context, @AnimRes itemAnimationRes: Int, key: Int): Animation {
        return animationArray[key] ?: AnimationUtils.loadAnimation(context, itemAnimationRes).apply {
                animationArray.put(key, this)
            }
    }

    /**
     * FrameLayout里面相邻的两个子View，进行Margin的平移，达到背景不移动，View移动的效果
     * title显示，content右移
     * 不能通过[android.view.animation.TranslateAnimation]，会把背景也移动
     *
     * @param titleView
     * @param contentView
     * @param duration
     */
    @JvmStatic
    fun marginLeftTranslateIn(titleView: View, contentView: View, duration: Int) {
        marginTranslateIn(titleView, contentView, duration, View.FOCUS_LEFT)
    }

    /**
     * FrameLayout里面相邻的两个子View，进行Margin的平移，达到背景不移动，View移动的效果
     * title隐藏，content全屏展示
     * 不能通过[android.view.animation.TranslateAnimation]，会把背景也移动
     *
     * @param titleView
     * @param contentView
     * @param duration
     */
    @JvmStatic
    fun marginLeftTranslateOut(titleView: View, contentView: View, duration: Int) {
        marginTranslateOut(titleView, contentView, duration, View.FOCUS_LEFT)
    }

    /**
     * FrameLayout里面相邻的两个子View，进行Margin的平移，达到背景不移动，View移动的效果
     * title显示，content右移
     * 不能通过[android.view.animation.TranslateAnimation]，会把背景也移动
     *
     * @param titleView
     * @param contentView
     * @param duration
     */
    @JvmStatic
    fun marginUpTranslateIn(titleView: View, contentView: View, duration: Int) {
        marginTranslateIn(titleView, contentView, duration, View.FOCUS_UP)
    }

    /**
     * FrameLayout里面相邻的两个子View，进行Margin的平移，达到背景不移动，View移动的效果
     * title隐藏，content全屏展示
     * 不能通过[android.view.animation.TranslateAnimation]，会把背景也移动
     *
     * @param titleView
     * @param contentView
     * @param duration
     */
    @JvmStatic
    fun marginUpTranslateOut(titleView: View, contentView: View, duration: Int) {
        marginTranslateOut(titleView, contentView, duration, View.FOCUS_UP)
    }

    /**
     * FrameLayout里面相邻的两个子View，进行Margin的平移，达到背景不移动，View移动的效果
     * title显示，content右移，同时让title可见，为了处理焦点
     * 不能通过[android.view.animation.TranslateAnimation]，会把背景也移动
     *
     * @param titleView
     * @param contentView
     * @param duration
     * @param direction [View.FOCUS_LEFT]
     * [View.FOCUS_UP]
     * [View.FOCUS_RIGHT]
     * [View.FOCUS_DOWN]
     */
    @JvmStatic
    fun marginTranslateIn(titleView: View, contentView: View, duration: Int, direction: Int) {
        val titleLayoutParams = titleView.layoutParams as ViewGroup.MarginLayoutParams
        val contentLayoutParams = contentView.layoutParams as ViewGroup.MarginLayoutParams
        var titleMargin = 0
        var contentMargin = 0
        var start = 0
        var end = 0
        var initValue = 0
        when (direction) {
            View.FOCUS_LEFT -> {
                titleMargin = titleLayoutParams.leftMargin
                contentMargin = contentLayoutParams.leftMargin
                start = titleMargin
                end = 0
                initValue = -titleMargin
            }
            View.FOCUS_UP -> {
                titleMargin = titleLayoutParams.topMargin
                contentMargin = contentLayoutParams.topMargin
                start = titleMargin
                end = 0
                initValue = -titleMargin
            }
            View.FOCUS_RIGHT -> {
                titleMargin = titleLayoutParams.rightMargin
                contentMargin = contentLayoutParams.rightMargin
                start = titleMargin
                end = 0
                initValue = -titleMargin
            }
            View.FOCUS_DOWN -> {
                titleMargin = titleLayoutParams.bottomMargin
                contentMargin = contentLayoutParams.bottomMargin
                start = titleMargin
                end = 0
                initValue = -titleMargin
            }
            else -> return
        }
        val finalTitleMargin = titleMargin
        val finalContentMargin = contentMargin
        val finalInitValue = initValue

        /**
         * -200 -> 0
         */
        val animator = ObjectAnimator.ofInt(start, end)
        animator.addUpdateListener { animation: ValueAnimator ->
            val value = finalInitValue + animation.animatedValue as Int
            when (direction) {
                View.FOCUS_LEFT -> {
                    titleLayoutParams.leftMargin = finalTitleMargin + value
                    contentLayoutParams.leftMargin = finalContentMargin + value
                }
                View.FOCUS_UP -> {
                    titleLayoutParams.topMargin = finalTitleMargin + value
                    contentLayoutParams.topMargin = finalContentMargin + value
                }
                View.FOCUS_RIGHT -> {
                    titleLayoutParams.rightMargin = finalTitleMargin + value
                    contentLayoutParams.rightMargin = finalContentMargin + value
                }
                View.FOCUS_DOWN -> {
                    titleLayoutParams.bottomMargin = finalTitleMargin + value
                    contentLayoutParams.bottomMargin = finalContentMargin + value
                }
                else -> return@addUpdateListener
            }
            titleView.layoutParams = titleLayoutParams
            contentView.layoutParams = contentLayoutParams
        }
        animator.addListener(object : AnimatorListenerAdapter() {
            override
            fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                titleView.visibility = View.VISIBLE
            }
        })
        animator.duration = duration.toLong()
        animator.start()
    }

    /**
     * FrameLayout里面相邻的两个子View，进行Margin的平移，达到背景不移动，View移动的效果
     * title隐藏，content全屏展示，同时让title不可见，为了处理焦点
     * 不能通过[android.view.animation.TranslateAnimation]，会把背景也移动
     *
     * @param titleView
     * @param contentView
     * @param duration
     * @param direction [View.FOCUS_LEFT]
     * [View.FOCUS_UP]
     * [View.FOCUS_RIGHT]
     * [View.FOCUS_DOWN]
     */
    @JvmStatic
    fun marginTranslateOut(titleView: View, contentView: View, duration: Int, direction: Int) {
        val titleLayoutParams = titleView.layoutParams as MarginLayoutParams
        val contentLayoutParams = contentView.layoutParams as MarginLayoutParams
        var titleMargin = 0
        var contentMargin = 0
        var start = 0
        var end = 0
        when (direction) {
            View.FOCUS_LEFT -> {
                titleMargin = titleLayoutParams.leftMargin
                contentMargin = contentLayoutParams.leftMargin
                start = titleMargin
                end = -titleView.width
            }
            View.FOCUS_UP -> {
                titleMargin = titleLayoutParams.topMargin
                contentMargin = contentLayoutParams.topMargin
                start = titleMargin
                end = -titleView.height
            }
            View.FOCUS_RIGHT -> {
                titleMargin = titleLayoutParams.rightMargin
                contentMargin = contentLayoutParams.rightMargin
                start = titleMargin
                end = -titleView.width
            }
            View.FOCUS_DOWN -> {
                titleMargin = titleLayoutParams.bottomMargin
                contentMargin = contentLayoutParams.bottomMargin
                start = titleMargin
                end = -titleView.height
            }
            else -> return
        }
        val finalTitleMargin = titleMargin
        val finalContentMargin = contentMargin

        /**
         * 0 -> -200
         */
        val animator = ObjectAnimator.ofInt(start, end)
        animator.addUpdateListener { animation: ValueAnimator ->
            val value = animation.animatedValue as Int
            when (direction) {
                View.FOCUS_LEFT -> {
                    titleLayoutParams.leftMargin = finalTitleMargin + value
                    /***
                     * 存在titleview 测量的宽度和实际配置的leftMargin不等，导致contentview左边有部分缺失
                     * 手动矫正，确保全部显示
                     */
                    var contentLeftMargin = finalContentMargin + value
                    if (contentLeftMargin < 0) {
                        contentLeftMargin = 0
                    }
                    contentLayoutParams.leftMargin = contentLeftMargin
                }
                View.FOCUS_UP -> {
                    titleLayoutParams.topMargin = finalTitleMargin + value
                    contentLayoutParams.topMargin = finalContentMargin + value
                }
                View.FOCUS_RIGHT -> {
                    titleLayoutParams.rightMargin = finalTitleMargin + value
                    /***
                     * 存在titleview 测量的宽度和实际配置的rightMargin不等，导致contentview左边有部分缺失
                     * 手动矫正，确保全部显示
                     */
                    var contentRightMargin = finalContentMargin + value
                    if (contentRightMargin < 0) {
                        contentRightMargin = 0
                    }
                    contentLayoutParams.rightMargin = contentRightMargin
                }
                View.FOCUS_DOWN -> {
                    titleLayoutParams.bottomMargin = finalTitleMargin + value
                    contentLayoutParams.bottomMargin = finalContentMargin + value
                }
                else -> return@addUpdateListener
            }
            titleView.layoutParams = titleLayoutParams
            contentView.layoutParams = contentLayoutParams
        }
        animator.addListener(object : AnimatorListenerAdapter() {
            override
            fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                titleView.visibility = View.GONE
            }
        })
        animator.duration = duration.toLong()
        animator.start()
    }

    /**
     * 在原基础上，移动View的外边距
     *
     * @param view
     * @param direction
     * @param margin
     */
    @JvmStatic
    fun marginByLayout(view: View, direction: Int, margin: Int) {
        val params = view.layoutParams as MarginLayoutParams
        when (direction) {
            View.FOCUS_LEFT -> params.leftMargin += margin
            View.FOCUS_UP -> params.topMargin += margin
            View.FOCUS_RIGHT -> params.rightMargin += margin
            View.FOCUS_DOWN -> params.bottomMargin += margin
            else -> return
        }
        view.layoutParams = params
    }

    /**
     * 设置View的外边距
     *
     * @param view
     * @param direction
     * @param margin
     */
    @JvmStatic
    fun marginToLayout(view: View, direction: Int, margin: Int) {
        val params = view.layoutParams as MarginLayoutParams
        when (direction) {
            View.FOCUS_LEFT -> params.leftMargin = margin
            View.FOCUS_UP -> params.topMargin = margin
            View.FOCUS_RIGHT -> params.rightMargin = margin
            View.FOCUS_DOWN -> params.bottomMargin = margin
            else -> return
        }
        view.layoutParams = params
    }

    /**
     * 变化View的高度动画
     */
    @JvmOverloads
    @JvmStatic
    fun heightToLayout(view: View,
                       fromH: Int = 0,
                       toH: Int = view.height,
                       duration: Long = ANIM_DURATION.toLong()) {
        val animator = ObjectAnimator.ofInt(fromH, toH)
        animator.addUpdateListener { animation: ValueAnimator ->
            val value = animation.animatedValue as Int
            val params = view.layoutParams
            params.height = value
            view.layoutParams = params
        }
        animator.duration = duration
        animator.start()
    }

    /**
     * 变化View的宽、高度动画
     */
    fun widthHeightToLayout(view: View,
                            targetHeight: Int,
                            startHeight: Int,
                            targetWidth: Int,
                            startWidth: Int,
                            isScaleDown:Boolean=true){

        val animator=ResizeAnimation(view, targetHeight, startHeight, targetWidth, startWidth, isScaleDown)
        animator.start()
    }


    /**
     * 放大缩小动画
     *
     * @param view
     * @param isScaled
     */
    @JvmOverloads
    @JvmStatic
    fun scaleView(view: View, isScaled: Boolean,
                  fromX: Float = 1F,
                  fromY: Float = 1F,
                  toX: Float = SCALE,
                  toY: Float = SCALE,
                  duration: Long = ANIM_DURATION.toLong()) {
        if (isScaled) {
            val animX = ObjectAnimator.ofFloat(view, SCALE_X, fromX, toX)
            val animY = ObjectAnimator.ofFloat(view, SCALE_Y, fromY, toY)
            val animatorSet = AnimatorSet()
            animatorSet.play(animX).with(animY)
            animatorSet.duration = duration
            animatorSet.start()
        } else {
            val animX = ObjectAnimator.ofFloat(view, SCALE_X, toX, fromX)
            val animY = ObjectAnimator.ofFloat(view, SCALE_Y, toY, fromY)
            val animatorSet = AnimatorSet()
            animatorSet.play(animX).with(animY)
            animatorSet.duration = duration
            animatorSet.start()
        }
    }

    /**
     * 平移动画
     */
    @JvmOverloads
    @JvmStatic
    fun translateView(view: View, translate: Boolean,
                      fromX: Float = 0F,
                      fromY: Float = 0F,
                      toX: Float,
                      toY: Float,
                      duration: Long = ANIM_DURATION.toLong()) {
        if (translate) {
            val animX = ObjectAnimator.ofFloat(view, TRANSLATION_X, fromX, toX)
            val animY = ObjectAnimator.ofFloat(view, TRANSLATION_Y, fromY, toY)
            // 解决ViewGroup中放大被遮挡问题
            val animZ = ObjectAnimator.ofFloat(view, TRANSLATION_Z, 0F, 0.05F)
            val animatorSet = AnimatorSet()
            animatorSet.play(animX).with(animY).with(animZ)
            animatorSet.duration = duration
            animatorSet.start()
        } else {
            val animX = ObjectAnimator.ofFloat(view, TRANSLATION_X, toX, fromX)
            val animY = ObjectAnimator.ofFloat(view, TRANSLATION_Y, toY, fromY)
            // 解决ViewGroup中放大被遮挡问题
            val animZ = ObjectAnimator.ofFloat(view, TRANSLATION_Z, 0.05F, 0F)
            val animatorSet = AnimatorSet()
            animatorSet.play(animX).with(animY).with(animZ)
            animatorSet.duration = duration
            animatorSet.start()
        }
    }

    /**
     * 透明度动画
     */
    @JvmOverloads
    @JvmStatic
    fun alphaView(view: View, alpha: Boolean,
                  from: Float = 1F,
                  to: Float,
                  duration: Long = ANIM_DURATION.toLong()) {
        if (alpha) {
            val animX = ObjectAnimator.ofFloat(view, ALPHA, from, to)
            animX.duration = duration
            animX.start()
        } else {
            val animX = ObjectAnimator.ofFloat(view, ALPHA, to, from)
            animX.duration = duration
            animX.start()
        }
    }


    /**
     * 获取一个视图移动动画
     * @param fromXType      动画开始前的 X 坐标类型, 取值范围为 [Animation.ABSOLUTE] 绝对位置、
     * [Animation.RELATIVE_TO_SELF] 以自身宽或高为参考、
     * [Animation.RELATIVE_TO_PARENT] 以父控件宽或高为参考
     * @param fromXValue     动画开始前的 X 坐标
     * @param toXType        动画结束后的 X 坐标类型
     * @param toXValue       动画结束后的 X 坐标
     * @param fromYType      动画开始前的 Y 坐标类型
     * @param fromYValue     动画开始前的 Y 坐标
     * @param toYType        动画结束后的 Y 坐标类型
     * @param toYValue       动画结束后的 Y 坐标
     * @param interpolator   动画周期
     * @param durationMillis 动画持续时间
     * @return 一个视图移动动画
     */
    fun getTranslateAnimation(
        fromXType: Int,
        fromXValue: Float,
        toXType: Int,
        toXValue: Float,
        fromYType: Int,
        fromYValue: Float,
        toYType: Int,
        toYValue: Float,
        interpolator: Interpolator?,
        durationMillis: Long
    ): TranslateAnimation {
        val translateAnimation = TranslateAnimation(
            fromXType, fromXValue, toXType, toXValue,
            fromYType, fromYValue, toYType, toYValue
        )
        translateAnimation.duration = durationMillis
        if (interpolator != null) {
            translateAnimation.interpolator = interpolator
        }
        return translateAnimation
    }

    /**
     * 获取一个视图移动动画
     * @param fromXDelta     动画开始的 X 轴坐标
     * @param toXDelta       动画结束的 X 轴坐标
     * @param fromYDelta     动画开始的 Y 轴坐标
     * @param toYDelta       动画结束的 Y 轴坐标
     * @param interpolator   动画周期
     * @param durationMillis 动画持续时间
     * @return 一个视图移动动画
     */
    fun getTranslateAnimation(
        fromXDelta: Float,
        toXDelta: Float,
        fromYDelta: Float,
        toYDelta: Float,
        interpolator: Interpolator?,
        durationMillis: Long
    ): TranslateAnimation {
        val translateAnimation = TranslateAnimation(
            fromXDelta, toXDelta, fromYDelta, toYDelta
        )
        translateAnimation.duration = durationMillis
        if (interpolator != null) {
            translateAnimation.interpolator = interpolator
        }
        return translateAnimation
    }


    // =
    /**
     * 获取一个视图摇晃动画
     * @param fromXDelta     动画开始的 X 轴坐标
     * @param toXDelta       动画结束的 X 轴坐标
     * @param cycles         动画周期 [CycleInterpolator]
     * @param durationMillis 动画持续时间
     * @return 一个视图摇晃动画
     */
    fun getShakeAnimation(
        fromXDelta: Float,
        toXDelta: Float,
        cycles: Float,
        durationMillis: Long
    ): TranslateAnimation {
        val interpolator: Interpolator? = if (cycles > 0.0f) CycleInterpolator(cycles) else null
        return getTranslateAnimation(
            fromXDelta, toXDelta, 0.0f, 0.0f,
            interpolator, durationMillis
        )
    }

    /**
     * 获取一个视图摇晃动画 ( 摇晃幅度为 10)
     * @param cycles         动画周期 [CycleInterpolator]
     * @param durationMillis 动画持续时间
     * @return 一个视图摇晃动画
     */
    fun getShakeAnimation(
        cycles: Float,
        durationMillis: Long
    ): TranslateAnimation {
        val interpolator: Interpolator? = if (cycles > 0.0f) CycleInterpolator(cycles) else null
        return getTranslateAnimation(
            0.0f, 10.0f, 0.0f, 0.0f,
            interpolator, durationMillis
        )
    }

    /**
     * 获取一个视图摇晃动画 ( 摇晃幅度为 10、持续 700 毫秒 )
     * @param cycles 动画周期 [CycleInterpolator]
     * @return 一个视图摇晃动画
     */
    fun getShakeAnimation(cycles: Float): TranslateAnimation {
        val interpolator: Interpolator? = if (cycles > 0.0f) CycleInterpolator(cycles) else null
        return getTranslateAnimation(
            0.0f, 10.0f, 0.0f, 0.0f,
            interpolator, 700
        )
    }

    /**
     * 获取一个视图摇晃动画 ( 摇晃幅度为 10、重复 7 次 )
     * @param durationMillis 动画持续时间
     * @return 一个视图摇晃动画
     */
    fun getShakeAnimation(durationMillis: Long): TranslateAnimation {
        return getTranslateAnimation(
            0.0f, 10.0f, 0.0f, 0.0f,
            CycleInterpolator(7f), durationMillis
        )
    }

    /**
     * 获取一个视图摇晃动画 ( 摇晃幅度为 10、重复 7 次、持续 700 毫秒 )
     * @return 一个视图摇晃动画
     */
    fun getShakeAnimation(): TranslateAnimation {
        return getTranslateAnimation(
            0.0f, 10.0f, 0.0f, 0.0f,
            CycleInterpolator(7f), 700
        )
    }

    fun popup(view: View, duration: Long): ObjectAnimator {
        view.alpha = 0f
        view.visibility = View.VISIBLE
        val popup = ObjectAnimator.ofPropertyValuesHolder(
            view,
            PropertyValuesHolder.ofFloat("alpha", 0f, 1f),
            PropertyValuesHolder.ofFloat("scaleX", 0f, 1f),
            PropertyValuesHolder.ofFloat("scaleY", 0f, 1f)
        )
        popup.duration = duration
        popup.interpolator = OvershootInterpolator()
        return popup
    }

    fun popout(
        view: View,
        duration: Long,
        animatorListenerAdapter: AnimatorListenerAdapter?
    ): ObjectAnimator {
        val popout = ObjectAnimator.ofPropertyValuesHolder(
            view,
            PropertyValuesHolder.ofFloat("alpha", 1f, 0f),
            PropertyValuesHolder.ofFloat("scaleX", 1f, 0f),
            PropertyValuesHolder.ofFloat("scaleY", 1f, 0f)
        )
        popout.duration = duration
        popout.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                view.visibility = View.GONE
                animatorListenerAdapter?.onAnimationEnd(animation)
            }
        })
        popout.interpolator = AnticipateOvershootInterpolator()
        return popout
    }

}

class ResizeAnimation(
    var view: View,
    private val targetHeight: Int,
    var startHeight: Int,
    private val targetWith: Int,
    var startWith: Int,
    var isScaleDown:Boolean
) : Animation() {
    override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
        super.applyTransformation(interpolatedTime, t)
        var newHeight=0
        var newWith=0
        if (isScaleDown){
            newHeight = (startHeight - targetHeight * interpolatedTime).toInt()
            newWith = (startWith  - targetWith * interpolatedTime).toInt()
        }else {
            newHeight = (startHeight + targetHeight * interpolatedTime).toInt()
            newWith = (startWith + targetWith * interpolatedTime).toInt()
        }
        if (targetHeight != 0)
            view.layoutParams.height = newHeight
        if (targetWith != 0)
            view.layoutParams.width = newWith
        view.requestLayout()
    }

    override fun willChangeBounds(): Boolean {
        return true
    }
}

interface AnimationScope {
    fun alphaIn(duration: Long = 300, mFrom: Float = 0f)
    fun scaleIn(duration: Long = 300, mFrom: Float = 0.5f)
    fun slideInBottom(duration: Long = 400)
    fun slideInLeft(duration: Long = 400)
    fun slideInRight(duration: Long = 400)
    fun popup(duration: Long = 400)
}

interface ObjectAnimationScope<T>:AnimationScope{
    fun animator(create:T.()->Animator)

    fun animator(config:T.(AnimatorSet)->Unit)
}

interface ViewAnimationScope:ObjectAnimationScope<View>{
    fun sizeAnimator(startHeight: Int=0,
                     startWidth: Int=0,
                     duration: Long=400,
                     isScaleDown:Boolean=true)
}
fun View.animation(config:ViewAnimationScope.()->Unit){
    val animatorSet = AnimatorSet()
    val view = this
    val animatorList = mutableListOf<Animator>()
    val scope=object:ViewAnimationScope{
        override fun alphaIn(duration: Long, mFrom: Float) {
            val animator = ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f)
            animator.duration = duration
            animator.interpolator = LinearInterpolator()
            animatorList.add(animator)
        }

        override fun scaleIn(duration: Long, mFrom: Float) {
            val scaleX = ObjectAnimator.ofFloat(view, "scaleX", mFrom, 1f)
            val scaleY = ObjectAnimator.ofFloat(view, "scaleY", mFrom, 1f)
            animatorSet.duration = duration
            animatorSet.interpolator = DecelerateInterpolator()
            animatorSet.play(scaleX).with(scaleY)
        }

        override fun slideInBottom(duration: Long) {
            val animator = ObjectAnimator.ofFloat(view, "translationY", view.measuredHeight.toFloat(), 0f)
            animator.duration = duration
            animator.interpolator = DecelerateInterpolator(1.3f)
            animatorList.add(animator)
        }

        override fun slideInLeft(duration: Long) {
            val animator = ObjectAnimator.ofFloat(view, "translationX", -view.rootView.width.toFloat(), 0f)
            animator.duration = duration
            animator.interpolator = DecelerateInterpolator(1.8f)
            animatorList.add(animator)
        }

        override fun slideInRight(duration: Long) {
            val animator = ObjectAnimator.ofFloat(view, "translationX", view.rootView.width.toFloat(), 0f)
            animator.duration = duration
            animator.interpolator = DecelerateInterpolator(1.8f)
            animatorList.add(animator)
        }

        override fun popup(duration: Long) {
           val animator=AnimationExt.popup(view,duration)
            animatorList.add(animator)
        }

        override fun sizeAnimator(startHeight: Int, startWidth: Int, duration: Long,isScaleDown: Boolean) {
            var animator = ObjectAnimator.ofInt(startHeight, view.height)
            animator.addUpdateListener { animation: ValueAnimator ->
                val value = animation.animatedValue as Int
                val params = view.layoutParams
                params.height = value
                view.layoutParams = params
            }
            animator.duration = duration
            animatorList.add(animator)
            animator = ObjectAnimator.ofInt(startWidth, view.width)
            animator.addUpdateListener { animation: ValueAnimator ->
                val value = animation.animatedValue as Int
                val params = view.layoutParams
                params.width = value
                view.layoutParams = params
            }
            animator.duration = duration
            animatorList.add(animator)
        }

        override fun animator(create: View.() -> Animator) {
            animatorList.add(create(view))
        }

        override fun animator(config: View.(AnimatorSet) -> Unit) {
            config(view,animatorSet)
        }

    }
    config(scope)
    animatorList.isNotEmpty().isTrue {
        animatorSet.playTogether(animatorList)
    }
    animatorSet.start()
}



interface AnimationListener {
    fun onStart()
    fun onStop()
}

abstract class RichAnimator<T : RichAnimator<T>> {
     var prev: T? = null
     var next: T? = null
     var duration: Long = -1
     var startDelay: Long = -1
     var interpolator: Interpolator? = null
     var repeatMode = RepeatMode.UNSET
     var repeatCount = -2
     var animatorSet: AnimatorSet? = null
     var animationListener: AnimationListener? = null

    abstract fun createAnimatorList(): List<Animator>

    private fun createAnimatorSet(): AnimatorSet {
        val animators = createAnimatorList()
        val animatorSet = AnimatorSet()
        animatorSet.playTogether(animators)
        if (duration != -1L) {
            animatorSet.duration = duration
        }
        if (startDelay != -1L) {
            animatorSet.startDelay = startDelay
        }
        if (interpolator != null) {
            animatorSet.interpolator = interpolator
        }
        animatorSet.addListener(object : Animator.AnimatorListener {
            override fun onAnimationStart(animation: Animator) {
                animationListener?.onStart()
            }

            override fun onAnimationEnd(animation: Animator) {
                animationListener?.onStop()
                next?.let {
                    it.prev = null
                    it.start()
                }
            }

            override fun onAnimationCancel(animation: Animator) {}
            override fun onAnimationRepeat(animation: Animator) {}
        })
        return animatorSet
    }

    fun start() {
        if (prev != null) {
            prev!!.start()
        } else {
            animatorSet = createAnimatorSet()
            animatorSet!!.start()
        }
    }

    fun cancel() {
        animatorSet?.cancel()
        next?.let {
            it.cancel()
            next = null
        }
    }

    enum class RepeatMode(val valueNum: Int) {
        RESTART(1), REVERSE(2), UNSET(-2);
    }

    companion object {
        /**
         * This value used with the repeatCount property to repeat
         * the animation indefinitely.
         */
        const val INFINITE = -1
    }
}

fun ValueAnimator.update(d: Drawable, block: (Float)-> Unit) {
    addUpdateListener {
        block.invoke(it.animatedFraction)
        d.invalidateSelf()
    }
}

fun ValueAnimator.duration(t: Long): ValueAnimator = apply {
    duration = t
}

fun ValueAnimator.delay(t: Long): ValueAnimator = apply {
    startDelay = t
}

fun ValueAnimator.repeat(): ValueAnimator = apply {
    repeatCount = ValueAnimator.INFINITE
    repeatMode = ValueAnimator.RESTART
}

fun ValueAnimator.toReverse(): ValueAnimator = apply {
    repeatCount = ValueAnimator.INFINITE
    repeatMode = ValueAnimator.REVERSE
}

fun ValueAnimator.interpolator(interpolator: TimeInterpolator) = apply {
    this.interpolator = interpolator
}

fun ValueAnimator.onEnd(block: () -> Unit) = apply {
    addListener(object: AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            block()
        }
    })
}

fun ValueAnimator.onCancel(block: () -> Unit) = apply {
    addListener(object: AnimatorListenerAdapter() {
        override fun onAnimationCancel(animation: Animator) {
            block()
        }
    })
}

fun ValueAnimator.onReset(block: () -> Unit) = apply {
    addListener(object: AnimatorListenerAdapter() {
        override fun onAnimationCancel(animation: Animator) {
            block()
        }

        override fun onAnimationEnd(animation: Animator) {
            block()
        }
    })
}


fun RecyclerView.ViewHolder.animationWithDelayOffset(
    isEnableAnimation: Boolean,
    @AnimRes itemAnimationRes: Int,
    delayOffset: Int,
    recyclerView: RecyclerView?
) {
    if (isEnableAnimation) {
        itemView.clearAnimation()
        val key = itemView.hashCode()
        val delay = recyclerView?.calculateAnimationDelay(adapterPosition, delayOffset)
        itemView.animation =
            loadAnimation(itemView.context, itemAnimationRes, key).apply {
                startOffset = delay?.toLong() ?: 0L
                if (hasEnded()) {
                    start()
                }
            }
        //recyclerView?.setTag(R.id.last_delay_animation_position, adapterPosition)
        /*with(getAdapter<LifecycleAdapter>()) {
            this?.registerLifeObserver(animationArrayKey, lifecycleObserver)
        }*/
    }
}

fun RecyclerView.ViewHolder.animation(
    isEnableAnimation: Boolean,
    @AnimRes itemAnimationRes: Int
) {
    if (isEnableAnimation) {
        itemView.clearAnimation()
        val key = itemView.hashCode() + 1
        itemView.animation = loadAnimation(itemView.context, itemAnimationRes, key).apply {
            if (this.hasEnded()) {
                this.start()
            }
        }
        /*with(getAdapter<LifecycleAdapter>()) {
            this?.registerLifeObserver(animationArrayKey, lifecycleObserver)
        }*/
    }
}

fun RecyclerView.Adapter<*>.into(
    recyclerView: RecyclerView,
    layoutManager: RecyclerView.LayoutManager? = null
) = apply {
    recyclerView.layoutManager = layoutManager ?: LinearLayoutManager(recyclerView.context)
    recyclerView.adapter = this
    /*if (this is LifecycleAdapter) {
        val context = recyclerView.context
        if (context is LifecycleOwner) {
            context.lifecycle.addObserver(this)
        }
    }*/
}

object InterpolatorExt{

    /**
     * 先加速后减速，这也是动画默认的Interpolator
     */
    val BASE_ACCELERATE_DECELERATE : Interpolator get() = AccelerateDecelerateInterpolator()

    /**
     * 匀速
     */
    val BASE_LINE : Interpolator get() = LinearInterpolator()

    /**
     * 初始速度为0，持续加速。它主要用在离场效果中，比如某个物体从界面中飞离，就可以用这种效果。
     * 它给人的感觉就会是「这货从零起步，加速飞走了」。
     * 到了最后动画骤停的时候，物体已经飞出用户视野，看不到了，所以他们是并不会察觉到这个骤停的。
     */
    val BASE_ACCELERATE : Interpolator get() = AccelerateInterpolator()

    /**
     * 初始速度很大，持续减速到速度为0。
     * 它的效果和上面这个 AccelerateInterpolator 相反，适用场景也和它相反：它主要用于入场效果，
     * 比如某个物体从界面的外部飞入界面后停在某处。它给人的感觉会是「咦飞进来个东西，让我仔细看看，哦原来是 XXX」。
     */
    val BASE_DECELERATE : Interpolator get() = DecelerateInterpolator()

    /**
     * 先回拉一下再进行正常动画轨迹。效果看起来有点像投掷物体或跳跃等动作前的蓄力。如果是平移动画，
     * 那么就是位置上的回拉；如果是放大动画，那么就是先缩小一下再放大；其他类型的动画同理。
     */
    val BASE_ANTICIPATE : Interpolator get() = AnticipateInterpolator()

    /**
     * 动画会超过目标值一些，然后再弹回来
     */
    val BASE_OVERSHOOT : Interpolator get() = OvershootInterpolator()

    /**
     * 开始前回拉，最后超过一些然后回弹。
     */
    val BASE_ANTICIPATE_OVERSHOOT : Interpolator get() = AnticipateOvershootInterpolator()

    /**
     * 在目标值处弹跳。有点像玻璃球掉在地板上的效果。
     */
    val BASE_BOUNCE : Interpolator get() = BounceInterpolator()

    /**
     * 这个也是一个正弦 / 余弦曲线，不过它和 AccelerateDecelerateInterpolator 的区别是，它可以自定义曲线的周期，所以动画可以不到终点就结束，
     * 也可以到达终点后回弹，回弹的次数由曲线的周期决定，
     * 曲线的周期由 CycleInterpolator() 构造方法的参数决定。
     * 下面两个图分别是参数为0.5f和2f的演示效果
     */
    fun BASE_CYCLE(cycles:Float) : Interpolator = CycleInterpolator(cycles)

    /**
     * Ease Interpolator
     */
    val EASE: Interpolator get() =  PathInterpolator(
        0.250f,
        0.100f,
        0.250f,
        1.000f
    )

    /**
     * Ease-In Interpolator
     */
    val EASE_IN: Interpolator get() = PathInterpolator(
        0.420f,
        0.000f,
        1.000f,
        1.000f
    )

    /**
     * Ease-Out Interpolator
     */
    val EASE_OUT: Interpolator get() = PathInterpolator(
        0.000f,
        0.000f,
        0.580f,
        1.000f
    )

    /**
     * Ease-In-Out Interpolator
     */
    val EASE_IN_OUT: Interpolator get() = PathInterpolator(
        0.420f,
        0.000f,
        0.580f,
        1.000f
    )

    object Cubic{
        /**
         * Ease-In-Cubic Interpolator
         */
        val EASE_IN_CUBIC: Interpolator get() = PathInterpolator(
            0.550f,
            0.055f,
            0.675f,
            0.190f
        )

        /**
         * Ease-Out-Cubic Interpolator
         */
        val EASE_OUT_CUBIC: Interpolator get() = PathInterpolator(
            0.215f,
            0.610f,
            0.355f,
            1.000f
        )

        /**
         * Ease-In-Out-Cubic Interpolator
         */
        val EASE_IN_OUT_CUBIC: Interpolator get() = PathInterpolator(
            0.645f,
            0.045f,
            0.355f,
            1.000f
        )

        val EASE_IN_OUT_CUBIC_TIME: TimeInterpolator get() = EaseInOutCubicInterpolator()
    }

    object Quart{
        /**
         * Ease-In-Quart Interpolator
         */
        val EASE_IN_QUART: Interpolator get() = PathInterpolator(
            0.895f,
            0.030f,
            0.685f,
            0.220f
        )
        /**
         * Ease-Out-Quart Interpolator
         */
        val EASE_OUT_QUART: Interpolator get() = PathInterpolator(
            0.165f,
            0.840f,
            0.440f,
            1.000f
        )

        /**
         * Ease-In-Out-Quart Interpolator
         */
        val EASE_IN_OUT_QUART: Interpolator get() = PathInterpolator(
            0.770f,
            0.000f,
            0.175f,
            1.000f
        )
    }

    object Quint{
        /**
         * Ease-In-Quint Interpolator
         */
        val EASE_IN_QUINT: Interpolator get() = PathInterpolator(
            0.755f,
            0.050f,
            0.855f,
            0.060f
        )

        /**
         * Ease-Out-Quint Interpolator
         */
        val EASE_OUT_QUINT: Interpolator get() = PathInterpolator(
            0.230f,
            1.000f,
            0.320f,
            1.000f
        )

        /**
         * Ease-In-Out-Quint Interpolator
         */
        val EASE_IN_OUT_QUINT: Interpolator get() = PathInterpolator(
            0.770f,
            0.000f,
            0.175f,
            1.000f
        )
    }

    object Sine{
        /**
         * Ease-In-Sine Interpolator
         */
        val EASE_IN_SINE: Interpolator get() = PathInterpolator(
            0.470f,
            0.000f,
            0.745f,
            0.715f
        )

        /**
         * Ease-Out-Sine Interpolator
         */
        val EASE_OUT_SINE: Interpolator get() = PathInterpolator(
            0.390f,
            0.575f,
            0.565f,
            1.000f
        )

        /**
         * Ease-In-Out-Sine Interpolator
         */
        val EASE_IN_OUT_SINE: Interpolator get() = PathInterpolator(
            0.445f,
            0.050f,
            0.550f,
            0.950f
        )
    }

    object Expo{
        /**
         * Ease-In-Expo Interpolator
         */
        val EASE_IN_EXPO: Interpolator get() = PathInterpolator(
            0.950f,
            0.050f,
            0.795f,
            0.035f
        )

        /**
         * Ease-Out-Exop Interpolator
         */
        val EASE_OUT_EXPO: Interpolator get() = PathInterpolator(
            0.190f,
            1.000f,
            0.220f,
            1.000f
        )

        /**
         * Ease-In-Out-Expo Interpolator
         */
        val EASE_IN_OUT_EXPO: Interpolator get() = PathInterpolator(
            1.000f,
            0.000f,
            0.000f,
            1.000f
        )
    }

    object Circ{
        /**
         * Ease-In-Circ Interpolator
         */
        val EASE_IN_CIRC: Interpolator get() = PathInterpolator(
            0.600f,
            0.040f,
            0.980f,
            0.335f
        )

        /**
         * Ease-Out-Circ Interpolator
         */
        val EASE_OUT_CIRC: Interpolator get() = PathInterpolator(
            0.075f,
            0.820f,
            0.165f,
            1.000f
        )

        /**
         * Ease-In-Out-Circ Interpolator
         */
        val EASE_IN_OUT_CIRC: Interpolator get() = PathInterpolator(
            0.785f,
            0.135f,
            0.150f,
            0.860f
        )
    }

    object Back{
        /**
         * Ease-In-Back Interpolator
         */
        val EASE_IN_BACK: Interpolator get()  = PathInterpolator(
            0.600f,
            -0.280f,
            0.735f,
            0.045f
        )

        /**
         * Ease-Out-Back Interpolator
         */
        val EASE_OUT_BACK: Interpolator get() = PathInterpolator(
            0.175f,
            0.885f,
            0.320f,
            1.275f
        )

        /**
         * Ease-In-Out-Back Interpolator
         */
        val EASE_IN_OUT_BACK: Interpolator get() = PathInterpolator(
            0.680f,
            -0.550f,
            0.265f,
            1.550f
        )
    }

    object Quad{
        /**
         * Ease-In-Quad Interpolator
         */
        val EASE_IN_QUAD: Interpolator get() = PathInterpolator(
            0.550f,
            0.085f,
            0.680f,
            0.530f
        )

        /**
         * Ease-Out-Quad Interpolator
         */
        val EASE_OUT_QUAD: Interpolator get() = PathInterpolator(
            0.250f,
            0.460f,
            0.450f,
            0.940f
        )

        /**
         * Ease-In-Out-Quad Interpolator
         */
        val EASE_IN_OUT_QUAD: Interpolator get() = PathInterpolator(
            0.455f,
            0.030f,
            0.515f,
            0.955f
        )
    }

    fun pathInterpolator(path: Path):Interpolator=PathInterpolator(path)

    // 裁切
    val Guillotine: TimeInterpolator get() = GuillotineInterpolator()

}

/**
 * Created by Dmytro Denysenko on 5/7/15.
 */
class GuillotineInterpolator : TimeInterpolator {
    override fun getInterpolation(t: Float): Float {
        return if (t < ROTATION_TIME) rotation(t) else if (t < ROTATION_TIME + FIRST_BOUNCE_TIME) firstBounce(
            t
        ) else secondBounce(t)
    }

    private fun rotation(t: Float): Float {
        return 4.592f * t * t
    }

    private fun firstBounce(t: Float): Float {
        return 2.5f * t * t - 3f * t + 1.85556f
    }

    private fun secondBounce(t: Float): Float {
        return 0.625f * t * t - 1.083f * t + 1.458f
    }

    companion object {
        const val ROTATION_TIME = 0.46667f
        const val FIRST_BOUNCE_TIME = 0.26666f
        const val SECOND_BOUNCE_TIME = 0.26667f
    }
}

class EaseInOutCubicInterpolator : TimeInterpolator {
    override fun getInterpolation(input1: Float): Float {
        var input = input1
        if (2.let { input *= it; input } < 1.0f) {
            return 0.5f * input * input * input
        }
        input -= 2f
        return 0.5f * input * input * input + 1
    }
}


/**
 * A path interpolator implementation compatible with API 4+.
 */
/*class PathInterpolatorDonut(path: Path) : Interpolator {
    private val mX: FloatArray
    private val mY: FloatArray

    init {
        val pathMeasure = PathMeasure(path, false *//* forceClosed *//*)
        val pathLength = pathMeasure.length
        val numPoints = (pathLength / PRECISION).toInt() + 1
        mX = FloatArray(numPoints)
        mY = FloatArray(numPoints)
        val position = FloatArray(2)
        for (i in 0 until numPoints) {
            val distance = i * pathLength / (numPoints - 1)
            pathMeasure.getPosTan(distance, position, null *//* tangent *//*)
            mX[i] = position[0]
            mY[i] = position[1]
        }
    }

    *//*constructor(controlX: Float, controlY: Float) : this(createQuad(controlX, controlY)) {}
    constructor(
        controlX1: Float, controlY1: Float,
        controlX2: Float, controlY2: Float
    ) : this(createCubic(controlX1, controlY1, controlX2, controlY2)) {
    }*//*

    override fun getInterpolation(t: Float): Float {
        if (t <= 0.0f) {
            return 0.0f
        } else if (t >= 1.0f) {
            return 1.0f
        }

        // Do a binary search for the correct x to interpolate between.
        var startIndex = 0
        var endIndex = mX.size - 1
        while (endIndex - startIndex > 1) {
            val midIndex = (startIndex + endIndex) / 2
            if (t < mX[midIndex]) {
                endIndex = midIndex
            } else {
                startIndex = midIndex
            }
        }
        val xRange = mX[endIndex] - mX[startIndex]
        if (xRange == 0f) {
            return mY[startIndex]
        }
        val tInRange = t - mX[startIndex]
        val fraction = tInRange / xRange
        val startY = mY[startIndex]
        val endY = mY[endIndex]
        return startY + fraction * (endY - startY)
    }

    companion object {
        *//**
         * Governs the accuracy of the approximation of the [Path].
         *//*
        private const val PRECISION = 0.002f
        fun createQuad(controlX: Float, controlY: Float): PathInterpolatorDonut {
            val path = Path()
            path.moveTo(0.0f, 0.0f)
            path.quadTo(controlX, controlY, 1.0f, 1.0f)
            return PathInterpolatorDonut(path)
        }

        fun createCubic(
            controlX1: Float, controlY1: Float,
            controlX2: Float, controlY2: Float
        ): PathInterpolatorDonut {
            val path = Path()
            path.moveTo(0.0f, 0.0f)
            path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0f, 1.0f)
            return PathInterpolatorDonut(path)
        }
    }
}*/

fun TimeInterpolator.keyFrame(vararg fractions: Float):Interpolator=KeyFrameInterpolator(this,*fractions)

class KeyFrameInterpolator(
    private val interpolator: TimeInterpolator,
    private vararg var fractions: Float
) : Interpolator {

    fun setFractions(vararg fractions: Float) {
        this.fractions = fractions
    }

    override fun getInterpolation(input1: Float): Float {
        var input = input1
        if (fractions.size > 1) {
            for (i in 0 until fractions.size - 1) {
                val start = fractions[i]
                val end = fractions[i + 1]
                val duration = end - start
                if (input in start..end) {
                    input = (input - start) / duration
                    return start + (interpolator.getInterpolation(input) * duration)
                }
            }
        }
        return interpolator.getInterpolation(input)
    }

    companion object {
        /*fun easeInOut(vararg fractions: Float): KeyFrameInterpolator {
            val interpolator = KeyFrameInterpolator(Ease.inOut())
            interpolator.setFractions(*fractions)
            return interpolator
        }*/

        /*fun pathInterpolator(
            controlX1: Float, controlY1: Float,
            controlX2: Float, controlY2: Float,
            vararg fractions: Float
        ): KeyFrameInterpolator {
            val interpolator = KeyFrameInterpolator(createCubic(controlX1, controlY1, controlX2, controlY2))
            interpolator.setFractions(*fractions)
            return interpolator
        }*/
    }
}

object TypeEvaluatorExt{

    fun picPoint( pointF1: PointF,  pointF2: PointF) = PicPointEvaluator(pointF1,  pointF2)

    fun paintPath( pointF: PointF) = PaintPathEvaluator(pointF)

}

/**
 * 利用网络上提供的算法计算出贝塞尔曲线上每一个点的坐标
 * Created by jackYang on 2017/1/1.
 */
class PaintPathEvaluator(private val flagPoint: PointF) : TypeEvaluator<PointF> {
    override fun evaluate(fraction: Float, startValue: PointF, endValue: PointF): PointF {
        return calculateBezierPointForQuadratic(
            fraction,
            startValue,
            flagPoint,
            endValue
        )
    }
}

/**
 * 通过构造传入两个控制点
 */
class PicPointEvaluator(private val flagPointF1: PointF, private val flagPointF2: PointF) : TypeEvaluator<PointF> {
    /**
     * 利用 CalculateBezierPointForCubic 算法计算出三阶贝塞尔曲线上任意点
     */
    override fun evaluate(fraction: Float, startValue: PointF, endValue: PointF): PointF {
        return calculateBezierPointForCubic(
            fraction, startValue,
            flagPointF1,
            flagPointF2, endValue
        )
    }
}